﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data.Objects.DataClasses;
using System.Collections;
using System.IO;
using System.Data.Objects;

namespace Infrastructure
{
    public class Reflector
    {

        private static Reflector reflector;

        public static Reflector Instance()
        {
            if (reflector == null)
                reflector = new Reflector();
            return reflector;
        }

        private Reflector() { }


        /// <summary>
        /// Return the value in specifed property 
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public  object GetPropertyValue(object obj, string propertyName)
        {
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.GetProperty;
            PropertyInfo property = obj.GetType().GetProperty(propertyName, flags);
            MethodInfo reader = property.GetGetMethod();
            return reader.Invoke(obj, null);
        }

        public object GetPropertyValue(object obj, PropertyInfo property)
        {
            MethodInfo reader = property.GetGetMethod();
            return reader.Invoke(obj, null);
        }


        public  bool IsEqual(object obj1, object obj2)
        {
            if (obj1 == null && obj2 != null)
                return false;
            else if (obj1 != null && obj2 == null)
                return false;
            else if (obj1 == null && obj2 == null)
                return true;
            else if (obj1.GetType() != obj2.GetType())
                return false;
            else
            {
                return obj1.Equals(obj2);
            }
        }

        public void InvokeNoParameters(object entity,string methodName,object[] parameters)
        {
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.GetProperty;
            Type[] parameterTypes = new Type[parameters.Count()];
            for (int i = 0; i < parameters.Count(); i++)
            {
                parameterTypes[0] = parameters[0].GetType();
            }
            MethodInfo method = entity.GetType().GetMethod(methodName, flags, null, parameterTypes, null);
            method.Invoke(entity, parameters);
        }

        /// <summary>
        /// Return the children in specified 
        /// </summary>
        /// <typeparam name="T">Type of chidren in parent object</typeparam>
        /// <param name="obj">The parenet object</param>
        /// <returns></returns>
        public  IEnumerable GetPropertyList<T>(object obj, string properyName)
        {
            object value = GetPropertyValue(obj, properyName);
            Type valueType = value.GetType();
            IEnumerable list = new System.Collections.ArrayList();
            if (valueType.GetGenericArguments()[0].IsSubclassOf(typeof(T)))
            {
                IEnumerable listObj = (IEnumerable)value;
                return listObj;
            }
            else
                return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T">Base Type which propertyInfos returned inherited</typeparam>
        /// <param name="obj">Source Obj</param>
        /// <returns></returns>
        public  IList<PropertyInfo> SearchProperties<T>(object obj)
        {
            List<PropertyInfo> properties = new List<PropertyInfo>();
            Type sourceType = obj.GetType();
            BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.GetProperty;
            PropertyInfo[] ps = sourceType.GetProperties(flags);
            foreach (PropertyInfo p in ps)
            {
                if (p.PropertyType.IsSubclassOf(typeof(T)))
                    properties.Add(p);
            }
            return properties;
        }

        public  void SetValue(object obj, string setterName, object value)
        {
            try
            {
                Type type = obj.GetType();
                PropertyInfo setter = type.GetProperty(setterName);
                Type finalType = setter.PropertyType;
                Type[] genericArguments=finalType.GetGenericArguments();
                
                MethodInfo actSetter = setter.GetSetMethod();
                if (genericArguments.Count() == 1 && finalType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    // contains Generic Parameter, it maybe a Nullable<> type
                    object finalValue = Convert.ChangeType(value, finalType.GetGenericArguments()[0]);
                    if (actSetter != null)
                        actSetter.Invoke(obj, new object[] { finalValue });
                }
                else
                {
                    object finalValue = Convert.ChangeType(value, finalType);
                    if (actSetter != null)
                        actSetter.Invoke(obj, new object[] { finalValue });
                }
            }
            catch (InvalidCastException ex)
            {
                string errorTemplate = "Set value failed in Property:{0} of Type:{1}";
                Exception exception = new Exception(String.Format(errorTemplate, setterName, value.GetType().Name), ex);
                throw exception;
                
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="resourceName">The name of MaifestResource</param>
        /// <param name="type">Used for Get Assembly</param>
        /// <returns></returns>
        public Stream GetManifestResource(string resourceName, Type type)
        {
            Assembly des = type.Assembly;
            return des.GetManifestResourceStream(resourceName);
        }
    }
}
